#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "defs.h"

volatile static int started = 0;

// start() jumps here in supervisor mode on all CPUs.
void
main()
{
  if(cpuid() == 0){
    consoleinit();
#if defined(LAB_PGTBL) || defined(LAB_LOCK)
    statsinit();
#endif
    printfinit();
    printf("\n");
    printf("xv6 kernel is booting\n");
    printf("\n");

    // freerange全部内存，释放到空闲链表free-list
    kinit();         // physical page allocator

    // 创建全局内核页表，然后加上一堆映射，uart还有内核代码之类的
    kvminit();       // create kernel page table

    // 加载内核页表物理地址到satp寄存器
    kvminithart();   // turn on paging

    // rv怎么打开分页的？
    // 内核阶段，物理地址==虚拟地址，所以算是直接映射，不用页表也能跑
    // 那内核函数调用的时候，栈怎么弄得？
    // 换句话说，调用函数的时候，底下的汇编是怎么搞的？栈不是要保存调用和变量以及地址吗
    // TODO csapp里面，查看函数调用的汇编代码学习一下啊

    // 进程是用proc[]数组管理的，
    // 初始化进程表里的进程，主要是锁的部分
    procinit();      // process table
    
    
    
    trapinit();      // trap vectors
    trapinithart();  // install kernel trap vector
    plicinit();      // set up interrupt controller

    
    plicinithart();  // ask PLIC for device interrupts
    binit();         // buffer cache
    iinit();         // inode cache
    fileinit();      // file table
    virtio_disk_init(); // emulated hard disk
#ifdef LAB_NET
    pci_init();
    sockinit();
#endif    

    // 第一个用户进程启动，
    // 每次分配的时候，都会调用allocproc，找到一个unused的proc
    // 这里会增加用户进程的内核页表-用户态页表，并且补充映射 ，分配内核栈
    // 里面包括了allocproc
    userinit();      // first user process

    
    __sync_synchronize();
    started = 1;
  } else {
    while(started == 0)
      ;
    __sync_synchronize();
    printf("hart %d starting\n", cpuid());
    kvminithart();    // turn on paging
    trapinithart();   // install kernel trap vector
    plicinithart();   // ask PLIC for device interrupts
  }

  scheduler();        
}
